///////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    FruitFrenzyGuts.cpp                                          //
//  Developer:   Tommy Riddle                                                 //
//  Create date: 03/18/2005                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Fruit Frenzy.                 //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2005 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "FruitFrenzyGuts.h"

////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Pears, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_PEAR ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_PEAR.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::SpinResult FruitFrenzyGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1			2					3						4						5
/* ICON_TICKET */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_TICKET,		EVT_FOUR_TICKET,		EVT_FIVE_TICKET		},
/* ICON_GOGGLES */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_GOGGLES,		EVT_FOUR_GOGGLES,		EVT_FIVE_GOGGLES	},
/* ICON_BALLOONS */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_BALLOONS,		EVT_FOUR_BALLOONS,		EVT_FIVE_BALLOONS	},
/* ICON_CANNON */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_CANNON,		EVT_FOUR_CANNON,		EVT_FIVE_CANNON		},
/* ICON_FRUIT_CAR */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_FRUIT_CAR,	EVT_FOUR_FRUIT_CAR,		EVT_FIVE_FRUIT_CAR	},
/* ICON_PLUM */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_PLUM,		EVT_THREE_PLUM,			EVT_FOUR_PLUM,			EVT_FIVE_PLUM		},
/* ICON_BANANA */		{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_BANANA,		EVT_THREE_BANANA,		EVT_FOUR_BANANA,		EVT_FIVE_BANANA		},
/* ICON_PEAR */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_PEAR,		EVT_THREE_PEAR,			EVT_FOUR_PEAR,			EVT_FIVE_PEAR		},
/* ICON_ORANGE */		{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_ORANGE,		EVT_THREE_ORANGE,		EVT_FOUR_ORANGE,		EVT_FIVE_ORANGE		},
/* ICON_STRAWBERRY */	{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_STRAWBERRY,	EVT_THREE_STRAWBERRY,	EVT_FOUR_STRAWBERRY,	EVT_FIVE_STRAWBERRY },
/* ICON_FRENZY */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_FRUIT */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_PINEAPPLE */	{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_PINEAPPLE,	EVT_THREE_PINEAPPLE,	EVT_FOUR_PINEAPPLE,		EVT_FIVE_PINEAPPLE	}
};

////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Frenzy, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_FRENZY ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_FRENZY.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::SpinResult FruitFrenzyGuts::m_aScatterWinSpinResults[][6] = 
{
// Number of icons:			0			1						2				3					4					5
/* ICON_TICKET */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_GOGGLES */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_BALLOONS */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_CANNON */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_FRUIT_CAR */	{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_PLUM */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_BANANA */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_PEAR */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_ORANGE */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_STRAWBERRY */	{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		},
/* ICON_FRENZY */		{	EVT_LOSER,	EVT_SCATTER_NEAR_MISS,	EVT_TWO_FRENZY,	EVT_THREE_FRENZY,	EVT_FOUR_FRENZY,	EVT_FIVE_FRENZY	},
/* ICON_FRUIT */		{	EVT_LOSER,	EVT_SCATTER_NEAR_MISS,	EVT_TWO_FRUIT,	EVT_THREE_FRUIT,	EVT_FOUR_FRUIT,		EVT_FIVE_FRUIT	},
/* ICON_PINEAPPLE */	{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER		}
};

////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aPayLineOffsets
///
/// Declare an array to look up the offset for a reel for each payline.
/// An offset of 0 means that the reel should stop on the middle row,
/// an offset of 1 means the reel should stop on the top row,
/// and an offset of -1 means the reel should stop on the bottom row.
/// So the offset is relative to the middle row, or pay line 1.
////////////////////////////////////////////////////////////////////////////
int8 FruitFrenzyGuts::m_aPayLineOffsets[25][5] = 
{
	{  0,  0,  0,  0,  0 },	// Pay line 1
	{  1,  1,  1,  1,  1 },	// Pay line 2
	{ -1, -1, -1, -1, -1 },	// Pay line 3
	{  1,  0, -1,  0,  1 },	// Pay line 4
	{ -1,  0,  1,  0, -1 },	// Pay line 5
	{  0,  1,  0,  1,  0 },	// Pay line 6
	{  0, -1,  0, -1,  0 },	// Pay line 7
	{  1,  0,  1,  0,  1 },	// Pay line 8
	{ -1,  0, -1,  0, -1 },	// Pay line 9
	{  0,  1,  1,  1,  0 },	// Pay line 10
	{  0, -1, -1, -1,  0 },	// Pay line 11
	{ -1, -1,  0, -1, -1 },	// Pay line 12
	{  1,  1,  0,  1,  1 },	// Pay line 13
	{ -1,  0,  0,  0, -1 },	// Pay line 14
	{  1,  0,  0,  0,  1 },	// Pay line 15
	{  1, -1,  1, -1,  1 },	// Pay line 16
	{ -1,  1, -1,  1, -1 },	// Pay line 17
	{  0,  0,  1,  0,  0 },	// Pay line 18
	{  0,  0, -1,  0,  0 },	// Pay line 19
	{ -1, -1,  1, -1, -1 },	// Pay line 20
	{  1,  1, -1,  1,  1 },	// Pay line 21
	{  1,  1,  0, -1, -1 },	// Pay line 22
	{ -1, -1,  0,  1,  1 },	// Pay line 23
	{  0,  1, -1,  1,  0 },	// Pay line 24
	{  0, -1,  1, -1,  0 }	// Pay line 25
};

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::FruitFrenzyGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::FruitFrenzyGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );

	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aScatterWinSpinResults array. 
	C_ASSERT( countof( m_aScatterWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::~FruitFrenzyGuts()
///
/// Destructor for FruitFrenzyGuts. 
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::~FruitFrenzyGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getNumLinesBet()
///
/// This game always has 25 lines bet in the feature, regardless of how
/// many lines were bet when the feature was triggered.
////////////////////////////////////////////////////////////////////////////
int32 FruitFrenzyGuts::getNumLinesBet()
{
	// If we are in normal mode...
	if ( getGameModeId() == MODE_NORMAL )
	{
		// then just defer to the base class.
		return SlotGuts::getNumLinesBet();
	}

	// We must be in free spin mode.
	ASSERT( getGameModeId() == MODE_FREE_SPIN );

	// All the lines are bet in free spin mode.
	return getPaylineCount();
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::SpinResult FruitFrenzyGuts::getPineappleLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Pineapple line win payout.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::SpinResult FruitFrenzyGuts::getPineappleLineWinResult( uint8* stops ) const
{
	// Count how many Pineapples we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Pineapple...
		if ( getIcon( getGameModeId(), i, stops[i] )->iconid != ICON_PINEAPPLE )
		{
			// then we have our total.
			break;
		}
    }

	// Use the array to look up the spin result for i Pineapples.
	return lookupLineWinSpinResult( ICON_PINEAPPLE, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::SpinResult FruitFrenzyGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::SpinResult FruitFrenzyGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, bool& bSubstitution )
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Pineapple...
		if ( currentIcon == ICON_PINEAPPLE )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		// Otherwise if the test icon is a Pineapple...
		else if ( testIcon == ICON_PINEAPPLE )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Pineapple.
			bSubstitution = true;

			// We have now found our first icon that is not a Pineapple.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
    }

	// If the testIcon is not one of the scatter symbols...
	if ( testIcon != ICON_FRUIT && testIcon != ICON_FRENZY )
	{
		// then we might have triggered the feature.

		// See if it is a 5 of a kind win...
		if ( i == 5 )
		{
			// and set the feature triggered flag if it is.
			setPaylineFeatureTrigger( line, pftFeatureTriggered );
		}
		// Otherwise see if it is a near miss...
		else if ( i == 4 )
		{
			// and set the near miss flag if it is.
			setPaylineFeatureTrigger( line, pftNearMiss );
		}
	}

	// If we made it all the way through and the testIcon is still a pineapple...
	if ( testIcon == ICON_PINEAPPLE )
	{
		// then this must be a 5 pineapple win, so reset the substitution flag.
		bSubstitution = false;
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool FruitFrenzyGuts::doesPineappleLineWinPayMore( SpinResult srPineapple, SpinResult srOther ) const
///
/// @param   srPineapple	The spin result to calculate the Pineapple payout for.
/// @param   srOther		The spin result to calculate the other payout for.
///
/// @return	 bool			True if the Pineapple win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool FruitFrenzyGuts::doesPineappleLineWinPayMore( SpinResult srPineapple,
	SpinResult srOther ) const
{
	// Lookup the payout for the Pineapple line win.
	long nPineappleLineWinPayout = getPayout( srPineapple )->payoutAmount;

	// Lookup the payout for the other line win.
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// Return true if the Pineapple payout is bigger.
    return ( nPineappleLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Track the Pineapple result separately since it might be changed to a substitution.
	SpinResult srPineappleLineWinResult = getPineappleLineWinResult( stops );

	// Now see what the payout is if we use any Pineapples we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Pineapples as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, line, testIcon, bSubstitution );

	// Use the larger payout between the regular Pineapple evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesPineappleLineWinPayMore( srPineappleLineWinResult,
		srResultWithPossibleSubstitution ) )
	{
		// The Pineapples by themselves pay out more so use the Pineapple result.
		rtnresult = srPineappleLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note.
			spinline->addNote( NOTE_PINEAPPLE_SUBSTITUTION );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		FruitFrenzyGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) ||
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getScatterWinResult()   
///
/// @param   spin				The current spin object.
/// @param   testIcon			The scatter icon that we are searching for.
/// @param   scatterPositions	The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::SpinResult FruitFrenzyGuts::getScatterWinResult( SlotSpin* spin, uint8 testIcon, int32& scatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	scatterPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == testIcon )
			{
				// then increase the icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( testIcon, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Initialize our multiplier for scatters.
		int32 multiplier = 1;

		// If we are in free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin scatter multiplier.
			multiplier = MULTIPLIER_FREE_SPIN_SCATTER;
		}

		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Add the payout (with multiplier) to the total win for this spin.
		increaseTotalWin( nPayout * multiplier );
    
		// Add a scatter to the spin.
		SlotScatter* pScatter = spin->addScatter( scatterPositions,
			nPayout * getBetPerLine(), multiplier, false, srScatterResult, 0 );

		// If the free spin multiplier was used...
		if ( multiplier == MULTIPLIER_FREE_SPIN_SCATTER )
		{
			// then add the free spin note.
			pScatter->addNote( NOTE_FREE_SPIN_SCATTER );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 void FruitFrenzyGuts::addDaredevilStrawberryScatters()
///
/// This helper method is used to add scatters when a 4 or 5 of a kind line
/// win occurs.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::addDaredevilStrawberryScatters( SlotSpin* spin )
{
	// Loop through all of the lines.
	for ( int n = 0; n < spin->getSpinLineCount(); n++ )
	{
		// See what kind of feature trigger the line had, if any.
		PaylineFeatureTrigger pft = getPaylineFeatureTrigger( n );

		// If the line triggered the feature...
		if ( pft == pftFeatureTriggered )
		{
			// then add the free game scatter.
			spin->addScatter( getPaylineScatterPosition( n ), 0, 0, false, EVT_FREE_GAME_FEATURE, 0 );

			// and increase the number of feature rounds.
			incrementNumFeatureRounds();
		}
		// Otherwise if the line had a near miss...
		else if ( pft == pftNearMiss )
		{
			// then add a near miss scatter.
			spin->addScatter( getPaylineScatterPosition( n ), 0, 0, false, EVT_FEATURE_NEAR_MISS, 0 );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::PaylineFeatureTrigger FruitFrenzyGuts::getPaylineFeatureTrigger()
///
/// This helper method is used to get the subgame data that tracks whether
/// a payline triggered the feature.
////////////////////////////////////////////////////////////////////////////
FruitFrenzyGuts::PaylineFeatureTrigger FruitFrenzyGuts::getPaylineFeatureTrigger( uint8 nPayline )
{
	// Make sure the payline is valid.
	ASSERT( nPayline < getPaylineCount() );

	// Get the subgame data for the payline.
	return (PaylineFeatureTrigger)m_subGame->getData( SGDIDX_PAYLINE_FEATURE_TRIGGER_0 + nPayline );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 void FruitFrenzyGuts::setPaylineFeatureTrigger()
///
/// This helper method is used to set the subgame data that tracks whether
/// a payline triggered the feature.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::setPaylineFeatureTrigger( uint8 nPayline, PaylineFeatureTrigger ePaylineResult )
{
	// Make sure the payline is valid.
	ASSERT( nPayline < getPaylineCount() );

	// Update the subgame data for the payline.
	m_subGame->setServerOnly( SGDIDX_PAYLINE_FEATURE_TRIGGER_0 + nPayline, true );
	m_subGame->update( SGDIDX_PAYLINE_FEATURE_TRIGGER_0 + nPayline, ePaylineResult );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 int32 FruitFrenzyGuts::getNumFeatureRounds()
///
/// This helper method is used to get the subgame data that tracks how many
/// feature rounds have been awarded.
////////////////////////////////////////////////////////////////////////////
int32 FruitFrenzyGuts::getNumFeatureRounds()
{
	// Return the subgame data.
	return m_subGame->getData( SGDIDX_NUM_FEATURE_ROUNDS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 void FruitFrenzyGuts::initializeNumFeatureRounds()
///
/// This helper method is used to initialize the subgame data that tracks
/// how many feature rounds have been awarded.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::initializeNumFeatureRounds()
{
	// Initialize the subgame data to 0.
    m_subGame->update( SGDIDX_NUM_FEATURE_ROUNDS, 0 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 void FruitFrenzyGuts::incrementNumFeatureRounds()
///
/// This helper method is used to increment the subgame data that tracks
/// how many feature rounds have been awarded.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::incrementNumFeatureRounds()
{
	// Get the current value and then add 1.
    m_subGame->update( SGDIDX_NUM_FEATURE_ROUNDS, getNumFeatureRounds() + 1 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 int32 FruitFrenzyGuts::getCurrentFeatureRound()
///
/// This helper method is used to get the subgame data that tracks the
/// current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
int32 FruitFrenzyGuts::getCurrentFeatureRound()
{
	// Return the subgame data.
	return m_subGame->getData( SGDIDX_CURRENT_FEATURE_ROUND );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::initializeCurrentFeatureRound()
///
/// This helper method is used to initialize the subgame data that tracks
/// the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::initializeCurrentFeatureRound()
{
	// Initialize the subgame data to 0.
    m_subGame->update( SGDIDX_CURRENT_FEATURE_ROUND, 0 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::incrementCurrentFeatureRound()
///
/// This helper method is used to increment the subgame data that tracks
/// the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::incrementCurrentFeatureRound()
{
	// Get the current value and then add 1.
    m_subGame->update( SGDIDX_CURRENT_FEATURE_ROUND, getCurrentFeatureRound() + 1 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getTotalPointsForRound()
///
/// This helper method is used to get the subgame data that tracks the
/// total points for the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
int32 FruitFrenzyGuts::getTotalPointsForRound()
{
	// Return the subgame data.
	return m_subGame->getData( SGDIDX_TOTAL_POINTS_FOR_ROUND );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::initializeTotalPointsForRound()
///
/// This helper method is used to initialize the subgame data that tracks
/// the total points for the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::initializeTotalPointsForRound()
{
	if(m_bDebugSpinsMode)
	{
	   m_subGame->setServerOnly( SGDIDX_TOTAL_POINTS_FOR_ROUND, true );
	   m_subGame->update( SGDIDX_TOTAL_POINTS_FOR_ROUND, 
							g_nFeatureWins == -1 ? 
							getRandomValueFromProbabilityTable() : g_nFeatureWins );
	}
	// Initialize the subgame data to a random value from the probabilty table.
	else
	{
		m_subGame->setServerOnly( SGDIDX_TOTAL_POINTS_FOR_ROUND, true );
		m_subGame->update( SGDIDX_TOTAL_POINTS_FOR_ROUND, getRandomValueFromProbabilityTable() );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getCurrentShotsRemaining()
///
/// This helper method is used to get the subgame data that tracks the
/// number of shots remaining for the current feature round that is being
/// played.
////////////////////////////////////////////////////////////////////////////
int32 FruitFrenzyGuts::getCurrentShotsRemaining()
{
	// Return the subgame data.
	return m_subGame->getData( SGDIDX_CURRENT_SHOTS_REMAINING );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::initializeCurrentShotsRemaining()
///
/// This helper method is used to initialize the subgame data that tracks
/// the number of shots remaining for the current feature round that is
/// being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::initializeCurrentShotsRemaining()
{
	// Each round starts out with 3 shots.
    m_subGame->update( SGDIDX_CURRENT_SHOTS_REMAINING, 3 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::decrementCurrentShotsRemaining()
///
/// This helper method is used to decrement the subgame data that tracks
/// the number of shots remaining for the current feature round that is
/// being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::decrementCurrentShotsRemaining()
{
	// Get the current value and subtract 1.
    m_subGame->update( SGDIDX_CURRENT_SHOTS_REMAINING, getCurrentShotsRemaining() - 1 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getCurrentPoints()
///
/// This helper method is used to get the subgame data that tracks the
/// points so far for the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
int32 FruitFrenzyGuts::getCurrentPoints()
{
	// Return the subgame data.
	return m_subGame->getData( SGDIDX_CURRENT_POINTS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::initializeCurrentPoints()
///
/// This helper method is used to initialize the subgame data that tracks
/// the points so far for the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::initializeCurrentPoints()
{
	// Initialize the subgame data to 0.
    m_subGame->update( SGDIDX_CURRENT_POINTS, 0 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::incrementCurrentPoints()
///
/// This helper method is used to increment the subgame data that tracks
/// the points so far for the current feature round that is being played.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::incrementCurrentPoints()
{
	// Get the current value and then add 1.
    m_subGame->update( SGDIDX_CURRENT_POINTS, getCurrentPoints() + 1 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getLastShotHit()
///
/// This helper method is used to get the subgame data that tracks 
/// whether or not the last shot was a hit.
////////////////////////////////////////////////////////////////////////////
bool FruitFrenzyGuts::getLastShotHit()
{
	// If the subgame data is true...
	if ( m_subGame->getData( SGDIDX_LAST_SHOT_HIT ) )
	{
		// then return true.
		return true;
	}

	// Otherwise return false.
	return false;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::setLastShotHit()
///
/// This helper method is used to set the subgame data that tracks 
/// whether or not the last shot was a hit.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::setLastShotHit( bool bLastShotHit )
{
	// Just store the new value in the subgame data.
    m_subGame->update( SGDIDX_LAST_SHOT_HIT, bLastShotHit );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See if there is a Fruit scatter.
	SpinResult srResult = getScatterWinResult( spin, ICON_FRUIT, nScatterPositions );

	// Add the scatter for Fruit, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	// Reset the scatter positions.
	nScatterPositions = 0;

	// See if there is a Frenzy scatter.
	srResult = getScatterWinResult( spin, ICON_FRENZY, nScatterPositions );

	// Add the scatter for Frenzy, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	// Add the scatters for the free game feature.
	addDaredevilStrawberryScatters( spin );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 FruitFrenzyGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
    return ( getGutsId() >= 1600 && getGutsId() < 1700 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		// then return the free spin multiplier...
        nGameModeMultiplier = MULTIPLIER_FREE_SPIN_LINE;

		// and the free spin note.
        nNoteId = NOTE_FREE_SPIN_LINE;
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8
FruitFrenzyGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	// Make sure we have shots remaining (in case somebody is trying to hack us)
	if ( getCurrentShotsRemaining() )
	{
		// The state will change, so a reply is required
		bReply = true;

		// Initialize a local to track whether or not this shot is a hit.
		bool bLastShotHit = false;
		// If the current points so far is equal to the total points for this round...
		if ( getCurrentPoints() == getTotalPointsForRound() )
		{
			// then always miss in this case.
			bLastShotHit = false;
		}
		// Otherwise if the current points plus all of the remaining shots is equal to the
		// total points for this round...
		else if ( getCurrentPoints() + getCurrentShotsRemaining() == getTotalPointsForRound() )
		{
			// then always hit in this case.
			bLastShotHit = true;
		}
		// Otherwise...
		else
		{
			// 50/50 chance of a hit in this case.
			if ( getRandomLessThan( 2 ) )
			{
				bLastShotHit = true;
			}
			else
			{
				bLastShotHit = false;
			}
		}

		if(m_bDebugSpinsMode)
		{
			Debug_isWinningShot( bLastShotHit );
		}

		// If the shot was a hit...
		if ( bLastShotHit )
		{
			// then give them another point.
			incrementCurrentPoints();
		}

		// Persist the local variable in the subgame data.
		setLastShotHit( bLastShotHit );

		// We just finished a shot, so subtract 1 from the counter.
		decrementCurrentShotsRemaining();

		// If we are out of shots for this round...
		if ( getCurrentShotsRemaining() == 0 )
		{
			//reset debug data
			g_nWinSequence = FruitFrenzyGuts::fwsNone;
			g_nFeatureWins = -1;

			// then declare a local variable to track how many spins
			// they win.
			int32 nNumSpins = 0;

			// If they scored 0 points...
			if ( getTotalPointsForRound() == 0 )
			{
				// then they get 5 spins.
				nNumSpins = 5;
			}
			// Otherwise if they scored 1 point...
			else if ( getTotalPointsForRound() == 1 )
			{
				// then they get 10 spins.
				nNumSpins = 10;
			}
			// Otherwise if they scored 2 points...
			else if ( getTotalPointsForRound() == 2 )
			{
				// then they get 15 spins.
				nNumSpins = 15;
			}
			// Otherwise if they scored on all 3 shots...
			else if ( getTotalPointsForRound() == 3 )
			{
				// then they get 25 free spins.
				nNumSpins = 25;
			}
			// Otherwise...
			else
			{
				// something bad is going on here.
				ASSERT( false );
			}

			// Award the free spins.
			incrementFreeSpins( nNumSpins );

			// Add the bonus game result.
			addBonusGame( 0, getCurrentFeatureRound(), 0, 0, 0, nNumSpins );

			// Go to the next feature round, if any.
			incrementCurrentFeatureRound();

			// If we still have another round to go...
			if ( getCurrentFeatureRound() < getNumFeatureRounds() )
			{
				// then reset everything for a new game.
				initializeTotalPointsForRound();
				initializeCurrentShotsRemaining();
				initializeCurrentPoints();
			}
			// Otherwise
			else
			{
				// the bonus game is done.
				setGameCanComplete( true );
				setBonusGameState( BONUS_GAME_INACTIVE );

				// Now start the free spin mode.
				// Note:  We might already be in free spin mode, but that is OK.
				setGameModeId( MODE_FREE_SPIN );
			}
		}
	}
 	
	return bReply;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Debugging code
//
////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden when running in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::preSpinClassifyResults( SlotSpin* spin )
{
	for ( uint8 n = 0; n < getPaylineCount(); n++ )
	{
		setPaylineFeatureTrigger( n, pftNone );
	}

	initializeNumFeatureRounds();
	initializeCurrentFeatureRound();

	SlotGuts::preSpinClassifyResults(spin);
	return;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::postSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to see if we can complete the game.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::postSpinClassifyResults( SlotSpin* spin )
{
	// If we have not reached the feature cap yet and we have feature rounds...
	if ( !getMaxPayoutAwarded() && getNumFeatureRounds() )
	{
		// then get ready to play the first feature round.
		initializeTotalPointsForRound();
		initializeCurrentShotsRemaining();
		initializeCurrentPoints();

		// The game can't complete until the bonus game is done.
		setGameCanComplete( false );
		setBonusGameState( BONUS_GAME_ACTIVE );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 FruitFrenzyGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=6;
	pStops[1]=0;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

void FruitFrenzyGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_RANDOM, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_WinRandom) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_ZERO, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_WinZero) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_ONE, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_WinOne) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_TWO, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_WinTwo) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_THREE, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_WinThree) );



	addDebugStopFunction( EVT_DEBUG_FUNCTION_HIT_FIRST, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_HitFirst) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_HIT_SECOND, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_HitSecond) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_HIT_THIRD, 
		new DebugStopFunc<FruitFrenzyGuts>(this, &FruitFrenzyGuts::DebugFeature_HitThird) );
}


void FruitFrenzyGuts::DebugFeature_WinRandom( SlotSpin *spin )
{
    g_nWinSequence = FruitFrenzyGuts::fwsNone;
    g_nFeatureWins = -1;
}

void FruitFrenzyGuts::DebugFeature_WinZero( SlotSpin *spin )
{
    g_nWinSequence = FruitFrenzyGuts::fwsNone;
    g_nFeatureWins = 0;
}

void FruitFrenzyGuts::DebugFeature_WinOne( SlotSpin *spin )
{
    g_nWinSequence = FruitFrenzyGuts::fwsNone;
    g_nFeatureWins = 1;
}

void FruitFrenzyGuts::DebugFeature_WinTwo( SlotSpin *spin )
{
    g_nWinSequence = FruitFrenzyGuts::fwsNone;
    g_nFeatureWins = 2;
}

void FruitFrenzyGuts::DebugFeature_WinThree( SlotSpin *spins )
{
    g_nWinSequence = FruitFrenzyGuts::fwsNone;
    g_nFeatureWins = 3;
}

//for hitting on specific shots
void FruitFrenzyGuts::DebugFeature_HitFirst( SlotSpin *spin )
{
    g_nWinSequence |= FruitFrenzyGuts::fwsFirst;
}

void FruitFrenzyGuts::DebugFeature_HitSecond( SlotSpin *spin )
{
    g_nWinSequence |= FruitFrenzyGuts::fwsSecond;
}

void FruitFrenzyGuts::DebugFeature_HitThird ( SlotSpin *spin )
{
    g_nWinSequence |= FruitFrenzyGuts::fwsThird;
}

////////////////////////////////////////////////////////////////////////////
// The current value for the number of times to win the subgame
// Value of -1 means we'll excercise no control over the subgame
////////////////////////////////////////////////////////////////////////////
int FruitFrenzyGuts::g_nFeatureWins = -1;

////////////////////////////////////////////////////////////////////////////
// If we're controlling feature game outcomes, which order do they win in?
// Used as a Bitmask.
////////////////////////////////////////////////////////////////////////////
int FruitFrenzyGuts::g_nWinSequence = FruitFrenzyGuts::fwsNone;

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FruitFrenzyGuts::isWinningShot()
///
/// @param   bool8   True/False indicating if this shot should win the feature.
/// This function only modifies the supplied value if the value of g_nFeatureWins
/// is > -1.
////////////////////////////////////////////////////////////////////////////
void FruitFrenzyGuts::Debug_isWinningShot(bool& value)
{
    if ( g_nFeatureWins > -1 )
    {
        value = getCurrentPoints() < g_nFeatureWins;
        if ( g_nWinSequence > FruitFrenzyGuts::fwsNone )
        {
            int currentShot = 3 - getCurrentShotsRemaining();
            switch ( currentShot )
            {
            case 0:
                value = ( g_nWinSequence & FruitFrenzyGuts::fwsFirst )
                          == FruitFrenzyGuts::fwsFirst ;
                break;
            case 1:
                value = ( g_nWinSequence & FruitFrenzyGuts::fwsSecond )
                          == FruitFrenzyGuts::fwsSecond ;
                break;
            case 2:
                value = ( g_nWinSequence & FruitFrenzyGuts::fwsThird )
                          == FruitFrenzyGuts::fwsThird ;
                break;
            }
        }
    }
}
